import java.io.*;
import java.lang.reflect.Array;
import java.net.*;
import java.util.ArrayList;
import java.util.Date;

/**
 * Created by ospip on 2017/11/26.
 * Copyright (c) 2017 LLAP. All rights reserved.
 */
public class ServerThread extends Thread {
    private Socket socket;
    private ArrayList<UserQueue> list;
    private int port = 1926;
    private Server ss;

    public ServerThread(ArrayList<UserQueue> l, int port, Server ss) {
        list = l;
        this.port = port;
        this.ss = ss;
    }
    public ServerThread(ArrayList<UserQueue> l, Server ss) {this(l, 1926, ss);}

    @Override
    public void run() {
        try {
            ServerSocket server = new ServerSocket(port);
            System.out.println("Start listening...");
            while (true) {
                Socket socket = server.accept();
//                System.out.println("Found client " + socket.getInetAddress().getHostAddress());
                new Thread(() -> {
                    String request = "";
                    BufferedReader br;
                    PrintWriter sout;
                    try {
                        String ip = socket.getInetAddress().getHostAddress();
                        br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        request = br.readLine();
//                        System.out.println(ip + ": " + request);

                        String[] args = request.toLowerCase().split("/");
                        if (args.length < 3) return;
                        String act, workerName;
                        int worker, queueNo, priority = 0;
                        worker = Integer.parseInt(args[0]);
                        act = args[1];
                        queueNo = Integer.parseInt(args[2]);
                        sout = new PrintWriter(socket.getOutputStream(), false);
                        if (act.equals("poll")) {
                            workerName = args[3];
//                            System.out.println(String.format("Request from worker %d: Calling queue %d to worker %s", worker, queueNo, workerName));
                            String ret;
                            if (list.get(queueNo).getSize() > 0) ret = list.get(queueNo).poll().getName();
                            else {
                                String group = list.get(queueNo).getGroup();
                                int maxNo = queueNo, maxSize = Integer.MIN_VALUE;
                                for (int i = 0; i < list.size(); i++) {
                                    if (list.get(i).getGroup().equals(group)) {
                                        if (list.get(i).getSize() > maxSize) {
                                            maxSize = list.get(i).getSize();
                                            maxNo = i;
                                        }
                                    }
                                }
                                ret = list.get(maxNo).poll().getName();
                            }
                            System.out.println("[" + new Date() + "] 请" + ret + "号到" + workerName);
                            new Thread(() -> {
                                String multiCastIP = "224.116.8.2";
                                int ttl = 1;
                                MulticastSocket msocket;
                                DatagramPacket pack;
                                InetAddress group;
                                int port = 817;
                                String message = "请" + ret + "号到" + workerName;
                                byte[] buf;

                                try {
                                    group = InetAddress.getByName(multiCastIP);
                                    msocket = new MulticastSocket(port);
                                    msocket.setTimeToLive(ttl);
                                    msocket.joinGroup(group);
                                    buf = message.getBytes();
                                    pack = new DatagramPacket(buf, buf.length, group, port);
                                    msocket.send(pack);
                                }
                                catch (Exception e) {
                                    System.out.println("Broadcast Error: " + e.getMessage());
                                }
                            }).start();
                            sout.println(ret);
                        }
                        else {
                            if (args.length > 3) {
                                priority = Integer.parseInt(args[3]);
                            }
//                            System.out.println(String.format("Request from worker %d: Adding to queue %d, priority %d", worker, queueNo, priority));
                            String ret = list.get(queueNo).insert(priority);
                            sout.println(ret);

                        }

                        sout.flush();
                        br.close();
                        sout.close();
                        ss.updateTable();
                    }
                    catch (Exception ex) {
                        System.out.println(ex.getMessage());
                    }
                    finally {
                        try {

                            socket.close();
                        }
                        catch (Exception ex) {
                            System.out.println("Internal Error: " + ex.getMessage());
                        }
                    }
                }).start();
            }
        }
        catch (Exception ex) {
            System.out.println("Server Error: " + ex.getMessage());
        }
    }

    public static void main(String[] args) throws IOException {
        int port = 1926;
        ArrayList<UserQueue> list = new ArrayList<UserQueue>();
        ServerSocket server = new ServerSocket(port);
        System.out.println("Waiting...");
        /* Args: %worker%/%act%/%queueNo%[/%workerName% | /%priority%]
        *  Example: 42/poll/一号窗口/1
        *  Example: 51/add/2
        *  Example: 51/add/2/1
        *
         */
        while (true) {
            Socket socket = server.accept();
            System.out.println("Found client " + socket.getInetAddress().getHostAddress());
            new Thread(new ServerThread(list, null)).start();
        }
    }
}
